package com.starsoft.trade.trader;

import java.io.InputStream;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.http.Header;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.starsoft.frame.util.DateUtil;
import com.starsoft.smdc.bean.SmdcSecurity;
import com.starsoft.trade.bean.TradeAcount;
import com.starsoft.trade.bean.TradeAcountStatus;
import com.starsoft.trade.bean.TradeBill;
import com.starsoft.trade.bean.TradePosition;
import com.starsoft.trade.bean.TradePositionId;
import com.starsoft.trade.bean.TradeSummary;
import com.starsoft.trade.bean.TradeSummaryId;
import com.starsoft.trade.order.Order;
import com.starsoft.trade.util.TradeType;

public class XqTrader extends AbstractTrader {
	
	private static Logger logger = LoggerFactory.getLogger(XqTrader.class);

	int historyPageSize = 20;

	String loginUrl = "https://xueqiu.com/user/login";
	String portflioUrl = "https://xueqiu.com/P/";
	String historyUrl = "https://xueqiu.com/cubes/rebalancing/history.json";
	String stockInfoUrl = "https://xueqiu.com/stock/p/search.json";
	String rebalanceUrl = "https://xueqiu.com/cubes/rebalancing/create.json";

	String username;
	String password;
	String portflioName;
	double multiply;
	
	enum HistoryStatusType{
		success, pending;
	}

	public void init(TradeAcount acount, Map<String, String> apiMap, TraderContextService service) {
		super.init(acount, apiMap, service);
		if(apiMap.containsKey("loginUrl")){
			loginUrl= apiMap.get("loginUrl");
		}
		if(apiMap.containsKey("portflioUrl")){
			portflioUrl= apiMap.get("portflioUrl");
		}
		if(apiMap.containsKey("historyUrl")){
			historyUrl= apiMap.get("historyUrl");
		}
		if(config.containsKey("username")){
			username = config.get("username");
		} else {
			throw new RuntimeException("未配置username");
		}
		if(config.containsKey("password")){
			password = config.get("password");
		} else {
			throw new RuntimeException("未配置password");
		}
		if(config.containsKey("portflioName")){
			portflioName = config.get("portflioName");
		} else {
			throw new RuntimeException("未配置portflioName");
		}
		if(config.containsKey("multiply")){
			multiply = Double.parseDouble(config.get("multiply"));
		} else {
			throw new RuntimeException("未配置multiply");
		}
	}

	public void login(){
		// 设置登陆时要求的信息，用户名和密码
		List<NameValuePair> params = new ArrayList<NameValuePair>();
		params.add(new BasicNameValuePair("areacode", "86"));
		params.add(new BasicNameValuePair("username", username));
		params.add(new BasicNameValuePair("password", password));
		String response = httpPost(loginUrl, defaultHeaders(), params);
		logger.info(response);
	}

	public TradeAcountStatus getPortflio() {
		Date asofDate = contextService.getPotflioDate();
		
		JsonNode root = getPotfloiNode();
		double netValue = root.get("net_value").asDouble();
		double cash = root.get("view_rebalancing").get("cash_value").asDouble();
		double profitDay = root.get("daily_gain").asDouble();
		JsonNode holdings = root.get("view_rebalancing").get("holdings");
		
		List<TradePosition> positions = new ArrayList<>();
		for (int i = 0; i < holdings.size(); i++){
			JsonNode holding = holdings.get(i);
			String secId = convertSecId(holding.get("stock_symbol").asText(), true);
			double volume = holding.get("volume").asDouble();
			TradePosition position = new TradePosition();
			position.setId(new TradePositionId(null, asofDate, secId));
			position.setSecNum(volume*multiply);
			positions.add(position);
		}
		TradeSummary summary = new TradeSummary();
		summary.setId(new TradeSummaryId(null, asofDate));
		summary.setNetAssert(netValue*multiply);
		summary.setCash(cash*multiply);
		summary.setMarketValue(summary.getNetAssert()-summary.getCash());
		summary.setProfit(netValue-1);
		summary.setProfitDay(profitDay);
		TradeAcountStatus status = new TradeAcountStatus(null, summary, positions);
		return contextService.fillStatus(acount, status);
	}

	private JsonNode getPotfloiNode() {
		String url = portflioUrl + portflioName;
		String body = httpGet(url, defaultHeaders());
		int pos_start = body.indexOf("SNB.cubeInfo = ") + 15;
		int pos_end = body.indexOf("SNB.cubePieData");
		String json_data = body.substring(pos_start, pos_end);
		return getJsonNode(json_data);
	}

	public List<TradeBill> getHistory(Date beginDate) {
		if(beginDate==null){
			throw new RuntimeException("begin Date can not be null");
		}
		List<TradeBill> bills = new ArrayList<TradeBill>();
		boolean stopFlag = false;
		for (int i = 0; i < 10 && !stopFlag; i++) {
			JsonNode list = getHistory(i + 1, historyPageSize);
			for (int j = 0; j < list.size() && !stopFlag; j++) {
				List<TradeBill> billList = getBillFromRebalance(list.get(j), HistoryStatusType.success);
				if (!billList.isEmpty() && billList.get(0).getBillTime().before(beginDate)) {
					stopFlag = true;
					break;
				}
				bills.addAll(billList);
			}
			if (list.size() < historyPageSize) {
				break;
			}
		}
		return contextService.fillBills(acount, bills);
	}

	private JsonNode getHistory(int page, int pageSize) {
		String url = String.format("%s?cube_symbol=%s&count=%s&page=%s", historyUrl, portflioName, pageSize, page);
		String body = httpGet(url, defaultHeaders());
		JsonNode root = getJsonNode(body);
		JsonNode list = root.get("list");
		return list;
	}

	private List<TradeBill> getBillFromRebalance(JsonNode rebalance, HistoryStatusType statusType) {
		List<TradeBill> bills = new ArrayList<TradeBill>();
		String status = rebalance.get("status").asText();
		if (!statusType.name().equals(status)) {
			return bills;
		}
		String category = rebalance.get("category").asText();
		JsonNode listRebalance = rebalance.get("rebalancing_histories");
		for (int k = 0; k < listRebalance.size(); k++) {
			JsonNode order = listRebalance.get(k);
			int id = order.get("id").asInt();
			String secId = convertSecId(order.get("stock_symbol").asText(), true);
			double price = order.get("price").asDouble();
			double secNum = order.get("volume").asDouble() - order.get("prev_volume").asDouble();
			secNum = secNum * multiply;
			if(secNum==0){
				continue;
			}
			Timestamp billTime = new Timestamp(order.get("updated_at").asLong());

			TradeBill bill = new TradeBill();
			if ("sys_rebalancing".equals(category)) {
				bill.setBillType(TradeType.stockdiv.name());
				bill.setBillTradeMoney(0.0);
			}

			bill.setExId(id + "");
			bill.setBillDesc("同步雪球," + DateUtil.toString(new Date()));
			bill.setSecId(secId);
			bill.setSecNum(secNum);
			bill.setSecPrice(price);
	
			bill.setBillTime(billTime);
			bills.add(bill);
		}
		return bills;
	}

	private Header[] defaultHeaders() {
		Header[] headers = new Header[10];
		headers[0] = new BasicHeader("User-Agent",
				"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:45.0) Gecko/20100101 Firefox/45.0");
		headers[1] = new BasicHeader("Host", "xueqiu.com");
		headers[2] = new BasicHeader("Pragma", "no-cache");
		headers[3] = new BasicHeader("Connection", "keep-alive");
		headers[4] = new BasicHeader("Accept", "*/*");
		headers[5] = new BasicHeader("Accept-Encoding", "gzip,deflate,sdch");
		headers[6] = new BasicHeader("Cache-Conrol", "no-cache");
		headers[7] = new BasicHeader("Referer", "http://xueqiu.com/P/ZH003694");
		headers[8] = new BasicHeader("X-Requested-With", "XMLHttpRequest");
		headers[9] = new BasicHeader("Accept-Language", "zh-CN,zh;q=0.8");
		return headers;
	}

	@Override
	public void trade(List<Order> orders) {
		JsonNode root = this.getPotfloiNode();
		
		double netValue = root.get("net_value").asDouble();
		JsonNode holdings = root.get("view_rebalancing").get("holdings");
		double cash = 100;

		Map<String, Order> flagMap = new HashMap<String, Order>();
		for (Order order : orders) {
			double volumn = order.getSecNum()*order.getPrice();
			double weight = volumn = volumn/multiply/netValue*100;
			order.setWeight(weight);
			flagMap.put(convertTicker(order.getSecId()), order);
		}
		for (int i = 0; i < holdings.size(); i++){
			JsonNode holding = holdings.get(i);
			String ticker = convertTicker(holding.get("stock_symbol").asText());
			if(flagMap.containsKey(ticker)){
				double targetWeight = holding.get("weight").doubleValue() + flagMap.get(ticker).getWeight();
				targetWeight = adjWeight(targetWeight);
				if(Math.abs(targetWeight)<1){
					targetWeight = 0;
				}
				((ObjectNode) holding).put("proactive", true);
				((ObjectNode) holding).put("weight", targetWeight);
				flagMap.remove(ticker);
			}
			cash -= holding.get("weight").doubleValue();
		}
		for(Order order : flagMap.values()){
			JsonNode holding = this.getStockInfo(order.getSecId());
			double targetWeight = adjWeight(order.getWeight());
			((ObjectNode) holding).put("weight", targetWeight);
			((ObjectNode) holding).put("proactive", true);
			((ObjectNode) holding).put("segment_name", holding.get("ind_name").textValue());
			((ArrayNode) holdings).add(holding);
			cash-= targetWeight;
		}
		
		List<NameValuePair> params = new ArrayList<NameValuePair>();
		params.add(new BasicNameValuePair("cash", adjWeight(cash)+""));
		params.add(new BasicNameValuePair("holdings", holdings.toString()));
		params.add(new BasicNameValuePair("cube_symbol", portflioName));
		params.add(new BasicNameValuePair("segment", "true"));
		String body = httpPost(rebalanceUrl, defaultHeaders(), params);
		System.out.println(body);
	}
	
	private JsonNode getStockInfo(String secId) {
		String ticker = convertSecId(secId, false);
		List<NameValuePair> params = new ArrayList<NameValuePair>();
		params.add(new BasicNameValuePair("code", ticker));
		params.add(new BasicNameValuePair("market", "cn"));
		String body = httpPost(stockInfoUrl, defaultHeaders(), params);
		JsonNode root = getJsonNode(body);
		return root.get("stocks").get(0);
	}

	private String convertSecId(String code, boolean toLocal) {
		String ticker = this.convertTicker(code);
		if (toLocal) {
			SmdcSecurity security = contextService.getSecurityByTicker(ticker);
			return security.getSecId();
		} else {
			return ticker;
		}
	}
	
	private String convertTicker(String code) {
		Pattern pa = Pattern.compile("(\\d+)");
		Matcher ma = pa.matcher(code);
		if(ma.find()){
			return ma.group();
		} else {
			return code;
		}
	}
	
	private double adjWeight(double value) {
		BigDecimal bd = new BigDecimal(value);
		return bd.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
	}


	@Override
	public List<TradeBill> getEntrust() {
		List<TradeBill> bills = new ArrayList<TradeBill>();
		JsonNode list = getHistory(1, 1);
		if(list.size()>0){
			bills =  getBillFromRebalance(list.get(0), HistoryStatusType.pending);
		}
		return bills;
	}

	@Override
	public void cancelEntrust(List<TradeBill> bills) {
		if(bills==null||bills.isEmpty()){
			return;	
		}
		
	}

	@Override
	public List<TradeBill> getHistory(InputStream is) {
		throw new RuntimeException("not support");
	}
}
